//cas过滤器验证
use actix_web::{  http, Error, HttpResponse};
use std::cell::RefCell;
use std::env;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};

use crate::webservice::cas::auth;
use actix_web::body::MessageBody;
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
use futures::future::{ok, Ready};
use futures::Future;
use qstring::QString;

// custom request auth middleware
pub struct CasAuth;

impl<S, B> Transform<S> for CasAuth
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    S::Future: 'static,
    B: MessageBody + 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Transform = CasAuthMiddleware<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(CasAuthMiddleware {
            service: Rc::new(RefCell::new(service)),
        })
    }
}

pub struct CasAuthMiddleware<S> {
    service: Rc<RefCell<S>>,
}

impl<S, B> Service for CasAuthMiddleware<S>
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    S::Future: 'static,
    B: MessageBody + 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }

    fn call(&mut self, req: ServiceRequest) -> Self::Future {
        let mut svc = self.service.clone();

        Box::pin(async move {
            println!("req.path().to_string() =={}", req.path().to_string());
            if req.path().to_string() == "/sso" {
                //获取访问地址
                let redirect_scheme = env::var("CAS_REDIRECT_SCHEME").unwrap();
                let host = req.headers().get("HOST").unwrap().to_str().unwrap();
                let url = format!("{}://{}{}", &redirect_scheme, &host, req.path().to_string());

                if auth::is_logined(&req).await {
                    Ok(svc.call(req).await?)
                } else {
                    //获取ticket
                    let query_str = req.query_string();
                    let qs = QString::from(query_str);
                    println!("query_str={}", &query_str);
                    if let Some(ticket) = qs.get("ticket") {
                        println!("ticket={}", &ticket);
                        //获取验证信息并写入redis
                        auth::set_logined_info_into_redis_by_ticket(&ticket, &url).await;

                        //获取验证信息
                        let validate_response =
                            auth::get_logined_info_from_redis_by_ticket(&ticket)
                                .await
                                .unwrap();
                        if validate_response.starts_with("yes") {
                            Ok(svc.call(req).await?)
                        } else {
                            //跳转到cas验证

                            println!("redirect_url={}", auth::get_redirect_url(&url));
                            Ok(req.into_response(
                                HttpResponse::Found()
                                    .header(http::header::LOCATION, auth::get_redirect_url(&url))
                                    .finish()
                                    .into_body(),
                            ))
                        }
                    } else {
                        //跳转到cas验证
                        println!("redirect_url={}", auth::get_redirect_url(&url));
                        Ok(req.into_response(
                            HttpResponse::Found()
                                .header(http::header::LOCATION, auth::get_redirect_url(&url))
                                .finish()
                                .into_body(),
                        ))
                    }
                }
            } else {
                Ok(svc.call(req).await?)
            }
        })
    }
}
